// 1. all：布尔全等判断
const all = (arr, fn = Boolean) => arr.every(fn);

all([ 4, 2, 3 ], (x) => x > 1); // true
all([ 1, 2, 3 ]); // true

// 2. approximatelyEqual：约等于
const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon;

approximatelyEqual(Math.PI / 2.0, 1.5708); // true

// 3. arrayToCSV：数组转CSV格式（带空格的字符串）

const arrayToCSV = (arr, delimiter = ',') => arr.map((v) => v.map((x) => `"${x}"`).join(delimiter)).join('\n');

arrayToCSV([ [ 'a', 'b' ], [ 'c', 'd' ] ]); // '"a","b"\n"c","d"'
arrayToCSV([ [ 'a', 'b' ], [ 'c', 'd' ] ], ';'); // '"a";"b"\n"c";"d"'

// 4. average：平均数
const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length;
average(...[ 1, 2, 3 ]); // 2
average(1, 2, 3); // 2

// 5. 检测数值出现次数
const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);
countOccurrences([ 1, 1, 2, 1, 2, 3 ], 1); // 3

// 6. deepFlatten：递归扁平化数组
const deepFlatten = (arr) => [].concat(...arr.map((v) => (Array.isArray(v) ? deepFlatten(v) : v)));

console.log(deepFlatten([ 1, [ 2 ], [ [ 3, [ 4 ] ] ], 5 ])); // [1,2,3,4,5]);

// 7. difference：寻找差异（并返回第一个数组独有的）

const difference = (a, b) => {
	const s = new Set(b);
	return a.filter((x) => !s.has(x));
};

difference([ 1, 2, 3 ], [ 1, 2, 4 ]); // [3]

// 8. dropWhile：删除不符合条件的值
const dropWhile = (arr, func) => {
	while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
	return arr;
};

console.log(dropWhile([ 1, 2, 3, 4 ], (n) => n >= 3)); // [3,4]

// 9. indexOfAll：返回数组中某值的所有索引
// 此代码段可用于获取数组中某个值的所有索引，如果此值中未包含该值，则返回一个空数组。
const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [ ...acc, i ] : acc), []);

console.log(indexOfAll([ 1, 2, 3, 1, 2, 3 ], 1)); // [0,3]
indexOfAll([ 1, 2, 3 ], 4); // []

// 10 intersection：两数组的交集

const intersection = (a, b) => {
	const s = new Set(b);
	return a.filter((x) => s.has(x));
};

intersection([ 1, 2, 3 ], [ 4, 3, 2 ]); // [2, 3]

// 11 intersectionWith：两数组都符合条件的交集
// 此片段可用于在对两个数组的每个元素执行了函数之后，返回两个数组中存在的元素列表。

const intersectionBy = (a, b, fn) => {
	const s = new Set(b.map(fn));
	return a.filter((x) => s.has(fn(x)));
};

intersectionBy([ 2.1, 1.2 ], [ 2.3, 3.4 ], Math.floor); // [2.1]

// 12 randomIntArrayInRange：生成两数之间指定长度的随机数组
const randomIntArrayInRange = (min, max, n = 1) =>
	Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min);

randomIntArrayInRange(12, 35, 10); // [ 34, 14, 27, 17, 30, 27, 20, 26, 21, 14 ]

// 13 sample：在指定数组中获取随机数
const sample = (arr) => arr[Math.floor(Math.random() * arr.length)];

sample([ 3, 7, 9, 11 ]); // 9

// 14 sampleSize：在指定数组中获取指定长度的随机数
// 此代码段可用于从数组中获取指定长度的随机数，直至穷尽数组。 使用Fisher-Yates算法对数组中的元素进行随机选择。
// 著名的洗牌算法：原理就是遍历数组元素，将当前元素与随机抽取的一个剩余元素进行交换
const sampleSize = ([ ...arr ], n = 1) => {
	let m = arr.length;
	while (m) {
		const i = Math.floor(Math.random() * m--);
		[ arr[m], arr[i] ] = [ arr[i], arr[m] ];
	}
	return arr.slice(0, n);
};

sampleSize([ 1, 2, 3 ], 2); // [3,1]
sampleSize([ 1, 2, 3 ], 4); // [2,3,1]

// 15 shuffle：“洗牌” 数组
// 此代码段使用Fisher-Yates算法随机排序数组的元素。

const shuffle = ([ ...arr ]) => {
	let m = arr.length;
	while (m) {
		const i = Math.floor(Math.random() * m--);
		[ arr[m], arr[i] ] = [ arr[i], arr[m] ];
	}
	return arr;
};

const foo = [ 1, 2, 3 ];
console.log(shuffle(foo)); // [2, 3, 1], foo = [1, 2, 3]

// 16 nest：根据parent_id生成树结构（阿里一面真题）
// 根据每项的parent_id，生成具体树形结构的对象。
const nest = (items, id = null, link = 'parent_id') =>
	items.filter((item) => item[link] === id).map((item) => ({ ...item, children: nest(items, item.id) }));
const comments = [
	{ id: 1, parent_id: null },
	{ id: 2, parent_id: 1 },
	{ id: 3, parent_id: 1 },
	{ id: 4, parent_id: 2 },
	{ id: 5, parent_id: 4 }
];
const nestedComments = nest(comments); // [{ id: 1, parent_id: null, children: [...] }]
console.log(nestedComments);
